using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Queries;
using Karma.Framework.Core.Domain;
using Karma.Framework.Core.Modules.Reports.Definition;
using Karma.Framework.Core.Repository;
using Karma.Framework.Core.Repository.Configuration;
using log4net;
using NHibernate;

namespace Karma.Framework.Core.Modules.Reports.Repository
{
    public class ReportRepository: RepositoryBase<Report>, IReportRepository
    {
        private static readonly ILog LOG = LogManager.GetLogger(typeof(ReportRepository).Name);

        #region Implementation of IReportRepository

        public IList<object> GetEntityProperty(string entityName, string propertyName)
        {
            var entityType = (from e in RepositoryConfiguration.Entities
                  where e.FullName.Equals(entityName)
                  select e).First();
            var propertyType = (from p in entityType.GetProperties()
                                where p.Name.Equals(propertyName)
                                select p).First();
            var query = new SimpleQuery(entityType, propertyType.PropertyType, string.Format(
                "select distinct(r.{0}) from {1} r", propertyName, entityType.Name));
            return ((object [])ActiveRecordMediator.ExecuteQuery(query)).ToList();
        }

        public IList<Report> GetEntityReports(string entityName)
        {
            var query = new SimpleQuery<Report>("from Report r where r.Entity.Name like :name");
            query.SetParameter("name", entityName);
            return query.Execute();
        }

        public ReportResult ExecuteReport(ReportSpecification specification)
        {
            if (specification.Metadata.Languaje == QueryLanguage.Hql)
            {
                return ExecuteHQL(specification);
            }
            return ExecuteSQL(specification);
        }

        #endregion

        public ReportResult ExecuteHQL(ReportSpecification specification)
        {
            var results = (IList)ActiveRecordMediator.Execute(
               typeof(DomainObject), delegate(ISession session, object instance)
               {
                   if (LOG.IsDebugEnabled)
                   {
                       LOG.Debug(string.Format("the expression: {0}", specification.Query));
                       LOG.Debug(string.Format("Start: {0}", specification.Start));
                       LOG.Debug(string.Format("Page size: {0}", specification.PageSize));
                   }
                   var multiQuery = session.CreateMultiQuery();
                   multiQuery.SetFlushMode(FlushMode.Never);
                   var index = specification.Query.IndexOf(" from");
                   if (LOG.IsDebugEnabled)
                   {
                       LOG.Debug(string.Format("index of [ from]: {0}", index));
                   }
                   var substr = specification.Query.Substring(index);
                   if (LOG.IsDebugEnabled)
                   {
                       LOG.Debug(string.Format("sub string: {0}", substr));
                   }
                   multiQuery.Add("select count(*) " + substr);

                   var query = session.CreateQuery(specification.Query);
                   if (specification.PageSize > 0)
                   {
                       query.SetFirstResult(specification.Start);
                       query.SetMaxResults(specification.PageSize);
                   }
                   multiQuery.Add(query);

                   foreach (var key in specification.Parameters.Keys)
                   {
                       if (typeof(ICollection).IsAssignableFrom(specification.Parameters[key].GetType()))
                       {
                           multiQuery.SetParameterList(key, (ICollection)specification.Parameters[key]);
                       }
                       else
                       {
                           multiQuery.SetParameter(key, specification.Parameters[key]);
                       }
                   }
                   return multiQuery.List();
               }, null);

            return new ReportResult
            {
                Count = (Int64)((IList)results[0])[0],
                Data = ((IList)results[1]).Cast<object>().ToArray()
            };
        }

        public ReportResult ExecuteSQL(ReportSpecification specification)
        {
            var results = (IList)ActiveRecordMediator.Execute(
               typeof(DomainObject), delegate(ISession session, object instance)
               {
                   if (LOG.IsDebugEnabled)
                   {
                       LOG.Debug(string.Format("the expression: {0}", specification.Query));
                       LOG.Debug(string.Format("Start: {0}", specification.Start));
                       LOG.Debug(string.Format("Page size: {0}", specification.PageSize));
                   }
                   var multiQuery = session.CreateMultiQuery();
                   multiQuery.SetFlushMode(FlushMode.Never);
                   var index = specification.Query.IndexOf(" from");
                   if (LOG.IsDebugEnabled)
                   {
                       LOG.Debug(string.Format("index of [ from]: {0}", index));
                   }
                   var substr = specification.Query.Substring(index);
                   if (LOG.IsDebugEnabled)
                   {
                       LOG.Debug(string.Format("sub string: {0}", substr));
                   }
                   var countQuery = session.CreateSQLQuery("select count(1) " + substr);
                   multiQuery.Add(countQuery);

                   var query = session.CreateSQLQuery(specification.Query);
                   if (specification.PageSize > 0)
                   {
                       query.SetFirstResult(specification.Start);
                       query.SetMaxResults(specification.PageSize);
                   }
                   multiQuery.Add(query);

                   foreach (var key in specification.Parameters.Keys)
                   {
                       if (typeof(ICollection).IsAssignableFrom(specification.Parameters[key].GetType()))
                       {
                           multiQuery.SetParameterList(key, (ICollection)specification.Parameters[key]);
                       }
                       else
                       {
                           multiQuery.SetParameter(key, specification.Parameters[key]);
                       }
                   }
                   return multiQuery.List();
               }, null);

            return new ReportResult
            {
                Count = (Int64)((IList)results[0])[0],
                Data = ((IList)results[1]).Cast<object>().ToArray()
            };
        }
    }
}